<!--
    __________    ____                   __  __           _      
   |  _____   |  / ___| _   _ _ __   ___|  \/  |_   _ ___(_) ___ 
   |  |    |  |  \___ \| | | | '_ \ / __| |\/| | | | / __| |/ __|
   |  |    |  |   ___) | |_| | | | | (__| |  | | |_| \__ \ | (__ 
  /   |   /   |  |____/ \__, |_| |_|\___|_|  |_|\__,_|___/_|\___|
 |___/   |___/          |___/                                    

 此项目使用 GPL v3 协议开放源代码，您可以在遵守本协议的前提下自由修改使用

 作者：Akkariin Meiko | QQ：204034 | Telegram：Akkariins

-->
<html>
	<head>
		<meta name="theme-color" content="#009688" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<meta http-equiv="X-UA-Compatible" content="IE=11">
		<title>SyncMusic - 在线点歌</title>
		<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
		<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/materialize-css@1.0.0/dist/css/materialize.min.css">
		<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css">
		<style type="text/css">body{font-size:15px;font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;font-weight:400;}hr{border:2px solid rgba(0,0,0,0.1);}.background-black{background:rgba(0,0,0,0.3);width:100%;height:100%;position:absolute;}.topbar-fixed{z-index:1;position:fixed;top:0px;left:0px;}.container{margin-top:80px;}.logo-text{font-size:22px ! important;font-weight:300;position: relative;top: -2px;}.description{font-size:18px;}.table tr td,.table tr th{padding-left:16px;padding-right:16px;font-weight:400;font-size:15px;}h1,h2,h3,h4,h5{font-weight:400;}.normal-text{font-size:15px;}.input{width:100%;}#chatdata{margin-top:16px;margin-bottom:16px;height:50vh;overflow-y:auto;}#chatdata .sysmsg{padding:4px 12px 4px 12px;border-radius:12px;margin:4px 0px 4px 0px;display:inline-block;background:rgba(0,0,0,0.5);color:#FFF;}#chatdata .chat{width:100%;margin-top:12px;}#chatdata .chat .headimg{display:inline-block;width:65px;text-align:center;vertical-align:top;}#chatdata .chat .headimg img{width:45px;transition-duration:0.25s;}#chatdata .chat .headimg img:hover{box-shadow:0px 0px 8px rgba(0,0,0,0.5);}#chatdata .chat .msgbox{display:inline-block;padding-left:6px;min-width:45px;max-width:calc(100% - 10px);}#chatdata .chat .msgbox small{display:block;}#chatdata .chat .msgbox .msg img{max-width:100%;max-height:100%;}#chatdata .chat .msgbox .msg{margin-top:4px;padding:12px;background:rgba(0,0,0,0.6);border-radius:8px;word-break:break-all;display:inline-block;min-width:45px;}#chatdata .chat .hiddentime{opacity:0;transition-duration:0.25s;}#chatdata .chat .hiddentime:hover{opacity:1;}#chatdata .hidechat{display:none;}.full-width{width:100%;}#musicname,#musiclrc{word-break:break-all;}#blur-img{position:fixed;height:120vh;left:-10%;width:120vw;overflow:hidden;top:-10%;z-index:-1;}.blur-mask{display:none;position:absolute;width:100%;height:100%;background-color:#000;filter:alpha(opacity=30);-moz-opacity:0.3;opacity:0.3;top:0;left:0;overflow:hidden;}.blured-img,#blurimgsrc{width:100%;height:100%;}.table tr td,.table tr th{white-space:nowrap;}::-webkit-scrollbar{width:10px;height:10px;background:rgba(0,0,0,0);}::-webkit-scrollbar-thumb{background:#009688;border-radius:8px;margin-left:2px;margin-right:2px;}::-webkit-scrollbar-corner{background:rgba(0,0,0,0);}.actionInput a{cursor:pointer;}.livechat{margin-bottom:80px;}</style>
	</head>
	<body class="blue-grey darken-4 white-text" onclick="checkElement(event)">
		<div id="blur-img" style="opacity: 1;">
			<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="blurred_flv8ppoyf" class="blured-img" viewBox="0 0 2304 978" preserveAspectRatio="none">
				<filter id="blur_flv8ppoyf">
					<feGaussianBlur in="SourceGraphic" stdDeviation="50"></feGaussianBlur>
				</filter>
				<image x="0" y="0" externalResourcesRequired="true" xlink:href="" id="blurimgsrc" style="filter:url(#blur_flv8ppoyf)" preserveAspectRatio="none"></image>
			</svg>
			<div class="blur-mask" style="display: block;"></div>
		</div>
		<div id="welcome" style="position: fixed;width: 100%;height: 100%;top: 0px;left: 0px;z-index: 9999999;background: #263238;">
			<span class="fa-stack fa-lg" style="font-size: 64px;position: fixed;top: calc(50% - 64px);left: calc(50% - 64px);cursor:pointer;" onclick="connect();$(welcome).fadeOut();">
				<i class="fa fa-circle fa-stack-2x" style="color:#009688 !important;"></i>
				<i class="fa fa-play fa-stack-1x fa-inverse" style="position: relative;left: 6px;"></i>
			</span>
		</div>
		<nav class="topbar-fixed">
			<div class="nav-wrapper teal">
				<ul id="nav-mobile" class="left">
					<li><a href="#" data-target="slide-out" class="sidenav-trigger waves-effect" style="display: inline-block;"><i class="material-icons">menu</i></a></li>
					<li><a href="#" class="logo-text waves-effect"> SyncMusic 在线点歌台</a></li>
				</ul>
			</div>
		</nav>
		<ul id="slide-out" class="sidenav">
			<li>
				<div class="user-view">
					<div class="background">
						<div class="background-black"></div>
						<img src="https://akariin.dev/images/background.jpg">
					</div>
					<a href="#"><img class="circle" src="https://akariin.dev/images/avatar.jpg"></a>
					<a href="#"><span class="white-text name">Akkariin Meiko</span></a>
					<a href="#"><span class="white-text email">个人开发者，鸽子王</span></a>
				</div>
			</li>
			<li><a class="waves-effect" href="https://tql.ink/" target="_blank">个人博客</a></li>
			<li><a class="waves-effect" href="https://t.me/Akkariins" target="_blank">Telegram</a></li>
			<li><div class="divider"></div></li>
			<li><a class="subheader">社交媒体</a></li>
			<li><a class="waves-effect" href="https://twitter.com/KasuganoSoras" target="_blank">Twitter</a></li>
			<li><a class="waves-effect" href="https://facebook.com/KasuganoSoras" target="_blank">Facebook</a></li>
			<li><a class="waves-effect" href="https://github.com/KasuganoSoras" target="_blank">GitHub</a></li>
			<li><a class="subheader">关于本项目</a></li>
			<li><a class="waves-effect" href="https://www.v2ex.com/t/617224" target="_blank">V2EX</a></li>
			<li><a class="waves-effect" href="https://github.com/kasuganosoras/SyncMusic" target="_blank">GitHub</a></li>
		</ul>
		<div class="container">
			<div class="row">
				<div class="col s12 m8">
					<div class="col s12 m4">
						<img src="" id="musicpic" title="点击静音/取消静音" onclick="setPause()" style="cursor:pointer;transition-duration:0.2s;display:none;width: 90%;padding: 4px;border: 32px solid #151515;border-radius: 50%;background: rgb(191, 20, 20);margin-top: 5%;margin-left:5%;box-shadow:0px 0px 8px #000;" onerror="this.style.display='none'" />
					</div>
					<div class="col s12 m8">
						<h5>正在播放</h5>
						<p id="musicname">Loading...</p>
						<br>
						<h5>歌词</h5>
						<p id="musiclrc">Loading...</p>
						<p><small id="usedtime">00:00:00</small><small id="endtime" class="right-align" style="display: block;margin-top: -18px;">00:00:00</small></p>
						<div class="progress">
							<div class="determinate" id="played" style="width: 0%"></div>
						</div>
						<p style="text-align:right;cursor:pointer;">
							<span onclick="if(volrange.style.display=='none'){$(volrange).fadeIn();}else{$(volrange).fadeOut();}">音量控制</span>&nbsp;&nbsp;&nbsp;
							<input type="range" value="1" min="0" max="1" step="0.01" id="volrange" 
								style="display:none;transition-duration: 0.2s;border: 0px;width: 30%;position: relative;top: -4px;" 
								onmousemove="musicControl.volume=this.value"
								onchange="localStorage.setItem('volume', this.value)" />
						</p>
					</div>
					<div class="col s12 m12">
						<br>
						<h5>播放列表</h5>
						<p>以下为等待播放的歌曲</p>
						<div style="overflow: auto;max-height:512px;">
							<table class="table responsive-table" id="musicList"></table>
						</div>
						<audio src="" id="musicControl" style="width: 0px;height: 0px;opacity: 0;" autoplay>
					</div>
				</div>
				<div class="col s12 m4 livechat">
					<h5>实时聊天</h5>
					<p>当前一共在线 <online id="online-user">0</online> 人</p>
					<div id="chatdata"><button onclick="connect()" class="btn btn-primary">连接服务器</button></div>
					<div class="input-group">
						<input type="text" id="msginput" class="form-control input white-text" placeholder="消息内容"></input>
						<span class="input-group-btn">
							<button onclick="sendmsg()" id="sendmsgbtn" class="btn btn-primary full-width">发送</button>
						</span>
					</div>
					<p class="actionInput">
						<a onclick="openedEmoji=true;$(faces).fadeIn();" id="openfaces">[ 发送表情 ]</a>&nbsp;&nbsp;
						<a onclick="msginput.value='投票切歌';sendmsg();">[ 投票切歌 ]</a>&nbsp;&nbsp;
						<a onclick="checkInput();" id="opensearch">[ 搜索音乐 ]</a>
					</p>
					<p>提示：输入 “点歌 歌名” 即可点歌。<br>例如输入：点歌 See You Again<br>支持输入网易云音乐 ID 来点歌</p>
					<p>遇到不好听的歌可以输入 “投票切歌”，当投票人数超过在线人数的 30% 时就会切歌。</p>
					<p>点播过于难听的歌曲例如 LostRiver 等将会被管理员切歌或从播放列表删除。</p>
					<p>输入 “设置昵称 名字” 即可设置自己的显示昵称，仅限当前客户端的 IP 地址有效。</p>
					<iframe src="/search.php" style="display: none;border: 0px;width: 100%;height: 512px;position: relative;top: -345px;background: #F1F1F1;border: 12px solid rgba(255,255,255,0.0);border-radius: 8px;box-shadow: 0px 0px 16px rgba(0,0,0,0.8);" id="search"></iframe>
					<iframe src="/face.html?s=3" style="display: none;border: 0px;width: 70vw;height: 70vh;position: fixed;top: 15vh;left: 15vw;background: #F1F1F1;border: 12px solid rgba(255,255,255,0.0);border-radius: 8px;box-shadow: 0px 0px 16px rgba(0,0,0,0.8);" id="faces"></iframe>
				</div>
			</div>
		</div>
	</body>
	<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@3.5.0/dist/jquery.min.js"></script>
	<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/materialize-css@1.0.0/dist/js/materialize.min.js"></script>
	<script type="text/javascript">
		// 配置
		var ws_hostname = 'ws://'+self.location.hostname+':811/';
		
		// 初始化变量
		var websocket;
		var lrcObj = {};
		var ws_connected = false;
		var last_lrc;
		var next_lrc;
		var rotate = 0;
		var paused = false;
		var openedEmoji = false;
		var lastVol = 0;
		var last_input = "";
		var inputGroupCss = "position: fixed;width:100%;bottom: 0px;left: 0px;padding-bottom:12px;padding-left: 12px;padding-right:12px;background:rgba(39,50,56,0.85);box-shadow: 0px 0px 8px rgba(0,0,0,0.5);";
		
		// 搜索功能
		function checkInput() {
			var userInput = $("#msginput").val();
			userInput = userInput.replace(/点歌 /g, "");
			if(userInput != "") {
				search.src = "search.php?s=" + encodeURIComponent(userInput);
				setTimeout("$(search).fadeIn();", 500);
				openedEmoji = true;
			}
		}
		
		// 暂停音乐
		function setPause() {
			if(paused) {
				musicControl.volume = lastVol;
				paused = false;
			} else {
				lastVol = musicControl.volume;
				musicControl.volume = 0;
				paused = true;
			}
		}
		
		// 关闭窗口
		function checkElement(event) {
			if(openedEmoji && event.target != faces && event.target != openfaces && event.target != search && event.target != opensearch) {
				$(faces).fadeOut();
				$(search).fadeOut();
				openedEmoji = false;
			}
		}
		
		// 系统消息
		function print(msg) {
			$("#chatdata").append("<center><span class='sysmsg'>" + msg + "</span></center>");
			chatdata.scrollTop = chatdata.scrollHeight;
		}
		
		// 解析 Lrc 格式歌词
		function parseLyric(text) {
			var lyrics = text.split("\n");
			var lrcObj = {};
			for(var i=0;i<lyrics.length;i++){
				var lyric = decodeURIComponent(lyrics[i]);
				var timeReg = /\[\d*:\d*((\.|\:)\d*)*\]/g;
				var timeRegExpArr = lyric.match(timeReg);
				if(!timeRegExpArr)continue;
				var clause = lyric.replace(timeReg,'');
				for(var k = 0,h = timeRegExpArr.length;k < h;k++) {
					var t = timeRegExpArr[k];
					var min = Number(String(t.match(/\[\d*/i)).slice(1)),
					sec = Number(String(t.match(/\:\d*/i)).slice(1));
					var time = min * 60 + sec;
					lrcObj[time] = clause;
				}
			}
			return lrcObj;
		}
		
		// 聊天内容
		function chat(data) {
			var regexp = new RegExp(/#([0-9]{1,2}).(jpg|png|gif)#/g);
			data.data = data.data.replace(regexp, "<img src='/face/$1.$2' />");
			var randid = Math.floor(Math.random() * 10000000);
			$("#chatdata").append("<div class='chat chat-" + randid + " hidechat'><div class='msgbox'><small class='name'>" + data.user + "</small><div class='msg' title='" + data.time + "'>" + data.data + "</div></div></div>");
			$(".chat-" + randid).fadeIn();
			chatdata.scrollTop = chatdata.scrollHeight;
		}
		
		// 读取音乐
		function music(data) {
			let music_title = data.artists + " - " + data.name;
			let setVolume = localStorage.getItem("volume");
			musicControl.volume = (setVolume == null || setVolume == undefined) ? 1 : setVolume;
			// 原本 pull request 这里加了个通知后面考虑还是去掉了，因为会多次显示刷屏
			document.title = music_title + " - SyncMusic - 在线点歌";
			musicControl.src = data.file;
			musicControl.pause();
			musicname.innerHTML = music_title + ", 专辑 《" + data.album + "》";
			musicpic.src = data.image;
			rotate = 0;
			$("#blurimgsrc").attr("xlink:href", data.image);
			$(musicpic).fadeIn();
			lrcObj = parseLyric(data.lrcs);
			setTimeout(function() {
				if(data.current != undefined) {
					musicControl.currentTime = data.current + 1;
				}
				musicControl.play();
			}, 1000);
		}
		
		// 转换时间格式
		function formatSeconds(value) {
			if(value == null || value == undefined) value = 0;
			var secondTime = parseInt(value);
			var minuteTime = 0;
			var hourTime = 0;
			if(secondTime > 60) {
				minuteTime = parseInt(secondTime / 60);
				secondTime = parseInt(secondTime % 60);
				if(minuteTime > 60) {
					hourTime = parseInt(minuteTime / 60);
					minuteTime = parseInt(minuteTime % 60);
				}
			}
			secondTime = parseInt(secondTime);
			minuteTime = parseInt(minuteTime);
			hourTime   = parseInt(hourTime);
			if(secondTime < 10) secondTime = "0" + parseInt(secondTime);
			if(minuteTime < 10) minuteTime = "0" + parseInt(minuteTime);
			if(hourTime < 10) hourTime = "0" + parseInt(hourTime);
			return hourTime + ":" + minuteTime + ":" + secondTime;
		}
		
		// 发送聊天消息
		function sendmsg() {
			var message = msginput.value;
			if(message == "") {
				alert("消息不能为空");
				return;
			}
			if(!ws_connected) {
				alert("请等待服务器连接");
				return;
			}
			var wait_send = {
				type: "msg",
				data: message
			};
			websocket.send(JSON.stringify(wait_send));
			msginput.value = "";
			msginput.disabled = true;
			sendmsgbtn.disabled = true;
			setTimeout(function() {
				msginput.disabled = false;
				sendmsgbtn.disabled = false;
				$(msginput).focus();
			}, 3000);
		}
		
		// 连接服务器
		function connect() {
			websocket = new WebSocket(ws_hostname);
			websocket.onopen = function (event) {
				$("#chatdata").html("");
				ws_connected = true;
				var userNick = window.localStorage.getItem("username");
				if(userNick != null && userNick != undefined) {
					websocket.send('{"type":"msg","data":"设置昵称 ' + userNick + '"}');
				}
			};
			websocket.onclose = function (event) {
				ws_connected = false;
				setTimeout("connect()", 5000);
			};
			websocket.onmessage = function (event) {
				handle(event.data);
			};
			websocket.onerror = function (event, e) {
				ws_connected = false;
			};
		}
		
		// 处理消息
		function handle(data) {
			try {
				var json = JSON.parse(data);
				if(json.type != undefined) {
					switch(json.type) {
						case "msg":
							print(json.data);
							break;
						case "chat":
							chat(json);
							break;
						case "music":
							music(json);
							break;
						case "list":
							musicList.innerHTML = json.data;
							break;
						case "online":
							$("#online-user").html(json.data);
							break;
						case "setname":
							window.localStorage.setItem("username", json.data);
							break;
						default:
							print("Unknown message type: " + json.type);
					}
				}
			} catch(e) {
				print(e.getMessage());
			}
		}
		
		// 网页加载完毕
		document.onreadystatechange = function() { 
			if(document.readyState == "complete") {
				$('.sidenav').sidenav();
				$("#msginput").keydown(function(e) {
					if(e.keyCode == 13){
						sendmsg();
					}
				});
				setInterval(function() {
					var curTime = musicControl.currentTime.toFixed();
					if(lrcObj[curTime] != undefined && lrcObj[curTime] != "") {
						musiclrc.innerText = lrcObj[curTime];
					}
				}, 1000);
				setInterval(function() {
					played.style.width = ((musicControl.currentTime / musicControl.duration) * 100) + "%";
					usedtime.innerText = formatSeconds(musicControl.currentTime);
					endtime.innerText = formatSeconds(musicControl.duration - musicControl.currentTime);
				}, 100);
				setInterval(function() {
					if(ws_connected) {
						websocket.send('{"type":"heartbeat"}');
					}
				}, 5000);
				setInterval(function() {
					if(!paused) {
						rotate = rotate + 1;
						musicpic.style.transform = "rotate(" + rotate + "deg)";
					}
					if(document.body.clientWidth <= 600) {
						$(".input-group").attr("style", inputGroupCss);
					} else {
						$(".input-group").attr("style", "");
					}
				}, 100);
			}
		}
	</script>
</html>
